home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / drdobbs / 1990 / 03 / wright.lst < prev   
File List  |  1990-02-13  |  19KB  |  740 lines

  1. MIXED-LANGUAGE PROGRAMMING WITH ASM
  2. by Karl Wright and Rick Schell
  3.  
  4. [LISTING ONE]
  5.  
  6.  
  7. ;* Module description * This module takes care of error trapping. The scheme 
  8. ;used records the trapping routine stack pointer so that an error can cause 
  9. ;the stack to return to a consistent state. This module was written using 
  10. ;Borland's Turbo Assembler 2.0.
  11.  
  12. ;** Environment **
  13. .model small    ;Set up for SMALL model.
  14. locals        ;Enable local symbols.
  15.  
  16. ;** Macros **
  17. ;<<Generate correct return based on model>>
  18. procret macro
  19. if @codesize
  20.     retf
  21. else
  22.     retn
  23. endif
  24. endm
  25.  
  26. ;** Public operations **
  27. public pascal ERROR_INIT    ;Initialize error handler.
  28. public pascal ERROR_TRAP    ;Set up error trap.
  29. public pascal ERROR_LOG        ;Log error.
  30.  
  31. ;** Uninitialized data **
  32. .data?
  33. errstk    dw ?    ;SP at last error log (-1 if none).
  34.  
  35. ;** Code **
  36. .code
  37. ;Set up DS to nothing since that is the typical arrangement.
  38. assume ds:nothing
  39.  
  40. ;[Initialize error manager]
  41. error_init proc pascal        ;Declare proc with PASCAL calling conventions.
  42.     mov errstk,-1
  43.     ret
  44. endp
  45.  
  46. ;[Set up error trap]
  47. ;This procedure preserves the previous ERRSTK, sets up a new ERRSTK, and
  48. ;calls the passed procedure.  On exit, the previous ERRSTK is restored.
  49. error_trap proc pascal        ;Pascal calling conventions.
  50. arg @@proc:codeptr        ;Only argument is procedure to call.
  51. uses ds,si,es,di        ;Force a save of all registers C cares for.
  52.     push errstk
  53.     ;Call internal routine to record return address on stack.
  54.     call @@rtn
  55.     pop errstk
  56.     ret
  57. @@rtn    label proc
  58.     mov errstk,sp        ;Save SP so we can restore it later.
  59.     call @@proc pascal    ;Call procedure.
  60.     xor ax,ax        ;Return code = 0 for normal return.
  61.     procret
  62. endp
  63.  
  64. ;[Log error]
  65. ;Control is passed to the last ERROR_TRAP, if any.
  66. ;Error code is passed and returned in AX.
  67. error_log proc pascal
  68. arg @@error_code:word
  69.     cmp errstk,-1        ;Lock up if no error address.
  70. @@1:    jz @@1
  71.     mov ax,@@error_code
  72.     mov sp,errstk
  73.     procret
  74. endp
  75. end
  76.  
  77.  
  78.  
  79. [LISTING TWO]
  80.  
  81. ;* Module description * This module manages a simple stack-based heap.
  82. ;Deallocation is not supported. NOTE: This module must be assembled with /MX 
  83. ;to publish symbols in the correct case. This module is written using 
  84. ;Borland's Turbo Assembler 2.0.
  85.  
  86. ;** Environment **
  87. .model small    ;Set up for SMALL model.
  88. locals        ;Enable local symbols.
  89.  
  90. ;** Equates **
  91. err_memory = 1    ;Out of memory error number.
  92.  
  93. ;** Public operations **
  94. public pascal HEAP_INIT        ;Initialize heap.
  95. public pascal HEAP_ALLOC    ;Allocate memory from heap.
  96.  
  97. ;** External operations **
  98.  
  99. ;<<Error handler>>
  100. extrn pascal ERROR_LOG:proc    ;Long jump library procedure for errors.
  101.  
  102. ;** Uninitialized data **
  103. .data?
  104. memptr    dw ?    ;Pointer to first free segment.
  105. memsiz    dw ?    ;Remaining paragraphs in heap.
  106.  
  107. ;** Code **
  108. .code
  109. ;Set up DS to nothing since that is the typical arrangement.
  110. assume ds:nothing
  111.  
  112. ;[Initialize the heap]
  113. heap_init proc pascal        ;Declare proc with PASCAL calling conventions.
  114. arg @@start_seg:word,@@para_size:word
  115.                 ;Arguments are starting segment and para count.
  116.     mov ax,@@start_seg
  117.     mov memptr,ax
  118.     mov ax,@@para_size
  119.     mov memsiz,ax
  120.     ret
  121. heap_init endp
  122.  
  123. ;[Allocate memory from the heap]
  124. heap_alloc proc pascal        ;Declare proc with PASCAL calling conventions.
  125. arg @@para_count:word        ;Only argument is count of paragraphs.
  126.     ;See if there is enough remaining.
  127.     mov ax,@@para_count
  128.     cmp memsiz,ax
  129.     jc @@err
  130.     sub memsiz,ax
  131.     add ax,memptr
  132.     xchg ax,memptr
  133.     mov dx,ax
  134.     xor ax,ax
  135.     ret
  136. @@err:    ;Out-of-memory error.
  137.     mov ax,err_memory
  138.     call error_log pascal,ax
  139.     ;Never returns.
  140. heap_alloc endp
  141.  
  142. end
  143.  
  144.  
  145. [LISTING THREE]
  146.  
  147. ;* Module description * This module reads source files and converts them into 
  148. ;words, then files the words away in a symbol table with the help of a hash 
  149. ;function. This module was written using Borland's Turbo Assembler 2.0.
  150.  
  151. ;** Environment **
  152. .model small    ;Set up for SMALL model.
  153. locals        ;Enable local symbols.
  154.  
  155. ;** Equates **
  156. ;<<Error numbers>>
  157. err_hash = 2    ;Out of hash space error number.
  158. err_read = 3    ;Read error.
  159.  
  160. ;<<Hash function>>
  161. hash_rotate = 5    ;Amount to rotate for hash function.
  162. hash_skip   = 11;Number of entries to skip on hash collision.
  163.  
  164. ;<<Read buffer>>
  165. rbf_size = 800h    ;Size of read buffer in paragraphs.
  166.  
  167. ;** Public operations **
  168. public pascal WORD_INIT        ;Initialize hash table.
  169. public pascal WORD_READ        ;Read file, convert to words, and hash them.
  170. public pascal WORD_COUNT    ;Get total word count.
  171. public pascal WORD_NAME        ;Get name of word.
  172. public pascal WORD_REFCOUNT    ;Get reference count of word.
  173. public pascal WORD_SCAN        ;Scan all words.
  174. public pascal WORD_COMPREF    ;Compare word reference counts.
  175.  
  176. ;** External operations **
  177. ;<<Heap>>
  178. extrn pascal HEAP_ALLOC:proc    ;Heap allocation.
  179.  
  180. ;<<Error handling>>
  181. extrn pascal ERROR_LOG:proc    ;Trap an error.
  182.  
  183. ;** Data structure **
  184. ;<<Symbol table entry>>
  185. symtbl struc
  186. symref    dw ?    ;Reference count.
  187. symsiz    dw ?    ;Length of word.
  188. ends
  189. symnam    = size symtbl    ;Offset of start of name text.
  190.  
  191. ;** Initialized data **
  192. .data
  193. ;<<Translation character type table>>
  194. typdlm    = 1    ;Delimiter bit.
  195. typnum    = 2    ;Numerical digit.
  196. typcas    = 20h    ;Lower case bit: Set if lower case letter.
  197. xlttbl    label byte
  198.     db '0' dup (typdlm)
  199.     db 10 dup (typnum)
  200.     db ('A'-1)-'9' dup (typdlm)
  201.     db 'Z'-('A'-1) dup (0)
  202.     db ('a'-1)-'Z' dup (typdlm)
  203.     db 'z'-('a'-1) dup (typcas)
  204.     db 255-'z' dup (typdlm)
  205.  
  206. ;** Uninitialized data **
  207. .data?
  208.  
  209. ;<<Hash table values>>
  210. hshptr    dw ?    ;Segment address of hash table.
  211. hshsiz    dw ?    ;Total number of hash entries.  Must be a power of 2!
  212. hshcnt    dw ?    ;Total free entries remaining in hash table.
  213. hshmsk    dw ?    ;Mask for converting hash value to address.
  214.  
  215. ;<<Read buffer values>>
  216. rbfptr    dw ?    ;Segment address of read buffer.
  217.  
  218. ;<<Word buffer>>
  219. wrdbuf    db 256 dup (?)
  220.  
  221. ;** Code **
  222. .code
  223. ;Set up DS to nothing since that is the typical arrangement.
  224. assume ds:nothing
  225.  
  226. ;[Initialize hash table]
  227. word_init proc pascal
  228. arg @@max_word_count:word    ;Argument: Maximum number of words.
  229. uses es,di
  230.     ;First, allocate read buffer.
  231.     mov ax,rbf_size
  232.     call heap_alloc pascal,ax
  233.     mov rbfptr,dx
  234.     ;Now convert maximum word count to power of 2.
  235.     mov ax,@@max_word_count
  236.     mov cl,16+1
  237. @@l1:    dec cl
  238.     shl ax,1
  239.     jnc @@l1
  240.     mov ax,1
  241.     shl ax,cl
  242.     ;Initialize some hash parameters.
  243.     mov hshsiz,ax
  244.     mov hshcnt,ax
  245.     dec ax
  246.     shl ax,1
  247.     mov hshmsk,ax
  248.     ;Now, allocate hash table from heap.
  249.     mov ax,hshsiz        ;Size of hash table in words.
  250.     add ax,7
  251.     mov cl,3
  252.     shr ax,cl        ;Convert to paragraphs.
  253.     call heap_alloc pascal,ax
  254.     mov hshptr,dx
  255.     ;Clear out hash table: 0 means 'no value'.
  256.     mov es,dx
  257.     xor di,di
  258.     cld
  259.     mov cx,hshsiz
  260.     xor ax,ax
  261.     rep stosw
  262.     ret
  263. word_init endp
  264.  
  265. ;[Read file and assimilate all words]
  266. word_read proc pascal
  267. arg @@handle:word        ;Argument is file handle.
  268. uses ds,si,es,di
  269.     ;Load XLAT buffer address.  The XLAT table is used for case conversion
  270.     ;and for character type identification.
  271.     mov bx,offset xlttbl
  272. @@read:    ;Read next buffer while delimiter processing.
  273.     call @@brd
  274.     jcxz @@done
  275. @@skip:    ;Skip all delimeters, etc.
  276.     lodsb
  277.     xlat xlttbl
  278.     test al,typdlm
  279.     loopnz @@skip
  280.     jnz @@read
  281.     ;Adjust pointer & count.
  282.     dec si
  283.     inc cx
  284.     ;If it is a number, skip to end.
  285.     test al,typnum
  286.     jnz @@num
  287.     ;It is a word. We'll transfer a word at a time to the word buffer, 
  288.         ;hashing it as we go. DX will be the current hash value. CX is the 
  289.         ;amount remaining in the buffer.
  290.     xor dx,dx
  291.     ;Initialize output address.
  292.     push ss
  293.     pop es
  294.     mov di,offset wrdbuf
  295. @@clp:    ;Transfer.  This is THE most time-critical loop in the program.
  296.     lodsb            ;Read character.
  297.     mov ah,al
  298.     xlat xlttbl        ;Get its type.
  299.     test al,typdlm        ;Abort if delimiter.
  300.     jnz @@wend
  301.     and al,typcas        ;Use case bit to convert to upper case.
  302.     neg al
  303.     add al,ah
  304.     stosb            ;Save it in word buffer.
  305.     ;Calculate hash value.
  306.     mov ah,cl
  307.     mov cl,hash_rotate
  308.     rol dx,cl
  309.     mov cl,ah
  310.     xor dl,al
  311.     loop @@clp        ;Keep going until end of buffer.
  312.     ;End of buffer while word processing.  Read more.
  313.     call @@brd
  314.     jcxz @@wnd2
  315.     jmp @@clp
  316. @@nrd:    ;Read next buffer while number processing.
  317.     call @@brd
  318.     jcxz @@done
  319. @@num:    ;Numbers are not considered 'words' and should be skipped.
  320.     ;Skip up to first delimiter.
  321.     lodsb
  322.     xlat xlttbl
  323.     test al,typdlm
  324.     loopz @@num
  325.     jz @@nrd
  326.     ;Adjust pointer and count.
  327.     dec si
  328.     inc cx
  329.     jmp @@skip
  330. @@done:    ret
  331. @@wend:    ;End of word.  Adjust buffer pointer.
  332.     dec si
  333. @@wnd2:    ;End of word.  Hash value is in DX, upper-case word is in WRDBUF,
  334.     ;DI points to end of word + 1.
  335.     push ds si cx bx    ;Save the registers we will use for this step.
  336.     xor al,al        ;Null-terminate the word.
  337.     stosb
  338.     mov cx,di        ;Calculate the word's length.
  339.     sub cx,offset wrdbuf
  340.     mov bx,dx        ;Put the hash value in a useable register.
  341.     shl bx,1        ;Lower bit will be discarded, so shift.
  342.     push ss            ;Initialize DS.
  343.     pop ds
  344.     assume ds:dgroup
  345.     ;Now it is time to locate the word in the hash table if it is there,
  346.     ;or create an entry if it is not.
  347. @@hlp:    mov es,hshptr
  348.     and bx,hshmsk
  349.     mov ax,es:[bx]
  350.     and ax,ax
  351.     jz @@make
  352.     ;Verify that the hash entry is the correct one.
  353.     mov es,ax
  354.     mov ax,cx
  355.     cmp es:[symsiz],ax    ;Compare length of word.
  356.     jnz @@coll
  357.     mov si,offset wrdbuf    ;Compare actual text if that agrees.
  358.     mov di,symnam
  359.     repz cmpsb
  360.     mov cx,ax
  361.     jz @@fd
  362. @@coll:    ;Collision!  Advance to the next candidate hash entry.
  363.     add bx,hash_skip*2
  364.     jmp @@hlp
  365. @@dne2:    ret
  366. @@make:    ;We have encountered this word for the first time.  
  367.     ;We must create a new symbol entry of the appropriate size.
  368.     ;First decrement remaining free hash count.
  369.     dec hshcnt
  370.     jz @@herr
  371.     push cx
  372.     push bx
  373.     mov ax,cx        ;Calculate length of symbol descriptor.
  374.     add ax,symnam+15
  375.     mov cl,4
  376.     shr ax,cl
  377.     call heap_alloc pascal,ax
  378.     pop bx            ;Record symbol descriptor in hash table.
  379.     mov es:[bx],dx
  380.     pop cx            ;Record length.
  381.     mov es,dx
  382.     mov es:[symsiz],cx
  383.     mov di,symnam        ;Move text of word into symbol table.
  384.     mov si,offset wrdbuf
  385.     shr cx,1
  386.     rep movsw
  387.     rcl cx,1
  388.     rep movsb
  389.     mov es:[symref],0    ;Clear reference count.
  390. @@fd:    ;Matching entry found!  Increment reference count.
  391.     inc es:[symref]
  392. @@nwd:    ;Go on to the next word in the buffer, if any.
  393.     pop bx cx si ds
  394.     assume ds:nothing
  395.     jcxz @@dne2
  396.     jmp @@skip    
  397. @@herr:    ;Out of hash space error.
  398.     mov ax,err_hash
  399.     call error_log pascal,ax
  400.     ;No return from ERROR_LOG.
  401. ;(Read buffer)
  402. ;Reads the next hunk of buffer.  Returns actual amount read in CX,
  403. ;DS:SI as start of data to read.
  404. @@brd:    push dx bx
  405.     mov cx,rbf_size*16
  406.     mov bx,@@handle
  407.     mov ah,3fh
  408.     mov ds,rbfptr
  409.     xor dx,dx
  410.     int 21h
  411.     jc @@err
  412.     mov cx,ax
  413.     xor si,si
  414.     pop bx dx
  415.     cld
  416.     retn        ;Use RETN so stack frame return won't be generated.
  417. @@err:    ;Read error.
  418.     mov ax,err_read
  419.     call error_log pascal,ax
  420.     ;No return is needed because ERROR_LOG never returns.
  421. word_read endp
  422.  
  423. ;[Get total word count]
  424. word_count proc pascal
  425.     mov ax,hshsiz        ;Load total word capacity.
  426.     sub ax,hshcnt        ;Subtract actual remaining free words.
  427.     ret
  428. word_count endp
  429.  
  430. ;[Get address of name of word]
  431. word_name proc pascal
  432. arg @@word_desc:word        ;Argument is word descriptor.
  433.     mov dx,@@word_desc
  434.     mov ax,symnam
  435.     ret
  436. word_name endp
  437.  
  438. ;[Get refcount for word]
  439. word_refcount proc pascal
  440. arg @@word_desc:word        ;Argument is word descriptor.
  441. uses ds
  442.     mov ds,@@word_desc
  443.     mov ax,ds:[symref]
  444.     ret
  445. word_refcount endp
  446.  
  447. ;[Scan all words]
  448. word_scan proc pascal
  449. arg @@scan_proc:codeptr        ;Argument is procedure to call for each word.
  450. uses ds,si
  451.     mov ds,hshptr
  452.     xor si,si
  453.     mov cx,hshsiz
  454.     cld
  455. @@l1:    lodsw
  456.     and ax,ax
  457.     jnz @@take
  458. @@next:    loop @@l1
  459.     ret
  460. @@take:    push cx ds
  461.     push ss
  462.     pop ds
  463.     call @@scan_proc pascal,ax
  464.     pop ds cx
  465.     cld
  466.     jmp @@next
  467. word_scan endp
  468.  
  469. ;[Compare reference counts for two word descriptors]
  470. word_compref proc pascal
  471. arg @@word_desc1:word,@@word_desc2:word
  472. uses ds
  473.     mov ds,@@word_desc2
  474.     mov ax,ds:[symref]
  475.     mov ds,@@word_desc1
  476.     sub ax,ds:[symref]
  477.     ret
  478. endp
  479. end
  480.  
  481.  
  482. [LISTING FOUR]
  483.  
  484. ;* Module description * This module contains the sort routine for SPECTRUM.
  485. ;This module was written using Borland's Turbo Assembler 2.0.
  486.  
  487. ;** Environment **
  488. .model small    ;Set up for SMALL model.
  489. locals        ;Enable local symbols.
  490.  
  491. ;** Public operations **
  492. public pascal SORT_DO        ;Perform sort.
  493.  
  494. ;** Code **
  495. .code
  496. ;Set up DS to nothing since that is the typical arrangement.
  497. assume ds:nothing
  498.  
  499. ;[Sort procedure]
  500. sort_do proc pascal
  501. arg @@array:dword,@@count:word,@@compare_proc:codeptr
  502. uses ds,si,di
  503.  
  504.     ;First load up registers for internal recursion.  DS:SI will be
  505.     ;the current sort array address, CX the count of elements to sort.
  506.     lds si,@@array
  507.     mov cx,@@count
  508.     call @@sort
  509.     ret
  510.  
  511. ;Internally recursive sort routine. This routine accepts DS:SI as the sort 
  512. ;array address, and CX as the count of elements to sort.
  513. @@sort:    cmp cx,2
  514.     jnc @@go
  515.     retn
  516. @@go:    ;Save all registers we will change.
  517.     ;Internally, DI and DX will be start and count of second merge area.
  518.     push si cx di dx
  519.     ;Divide into two parts and sort each one.
  520.     mov dx,cx
  521.     shr cx,1
  522.     sub dx,cx
  523.     call @@sort
  524.     mov di,si
  525.     add di,cx
  526.     add di,cx
  527.     xchg si,di
  528.     xchg cx,dx
  529.     call @@sort
  530.     xchg cx,dx
  531.     xchg si,di
  532.     ;Now, merge the two areas in place.
  533.     ;Each area must be at least size 1.
  534. @@mrgl:    ;Compare - DS:DI - DS:SI.
  535.     call @@compare_proc pascal,ds:[di],ds:[si]
  536. ;;The following commented-out sequence is the code that would be required
  537. ;;if strict Pascal calling conventions were adhered to for calling
  538. ;;COMPARE_PROC.  You can see how much extra work this is!!
  539. ;;    push cx dx
  540. ;;    push ds
  541. ;;    mov ax,ds:[di]
  542. ;;    mov bx,ds:[si]
  543. ;;    push ss
  544. ;;    pop ds
  545. ;;    call @@compare_proc pascal,ax,bx
  546. ;;    pop ds
  547. ;;    pop dx cx
  548. ;;    and ax,ax
  549.     jns @@ok
  550.     ;Slide up first merge area using starting value from DI.
  551.     mov ax,ds:[di]
  552.     push si cx
  553. @@sllp:    xchg ax,ds:[si]
  554.     add si,2
  555.     loop @@sllp
  556.     xchg ax,ds:[si]
  557.     pop cx si
  558.     add si,2
  559.     add di,2
  560.     dec dx
  561.     jnz @@mrgl
  562.     jmp short @@exi
  563. @@ok:    ;Correct so far.  Advance SI.
  564.     add si,2
  565.     loop @@mrgl
  566. @@exi:    ;Restore registers.
  567.     pop dx di cx si
  568.     retn
  569. sort_do endp
  570.  
  571. end
  572.  
  573.  
  574. [LISTING FIVE]
  575.  
  576. /***** File: SPECTRUM.C *****/
  577. /* This C module is written using Borland's Turbo C 2.0 and can be
  578.    compiled using the default switches.  It should be linked with the file
  579.    WILDARGS.OBJ from the Turbo C examples directory to enable the wildcard
  580.    file name expansion facility.  Without WILDARGS, SPECTRUM will still work
  581.    but will not be capable of expanding file names with wildcards.
  582.  
  583.    The following is an example make file, where TA is the assembler name, TCC
  584.    is the C compiler name, TLINK is the linker name, \TC\LIB contains the C
  585.    libraries, and \TC\EXA contains the Turbo C examples:
  586.  
  587. spectrum.exe: spectrum.obj heap.obj word.obj error.obj sort.obj
  588.    tlink \tc\lib\c0s+\tc\exa\wildargs+spectrum+heap+word+error+sort,spectrum,,\tc\lib\cs.lib;
  589. heap.obj: heap.asm
  590.     ta heap /mx;
  591. word.obj: word.asm
  592.     ta word /mx;
  593. error.obj: error.asm
  594.     ta error /mx;
  595. sort.obj: sort.asm
  596.     ta sort /mx;
  597. spectrum.obj: spectrum.c
  598.     tcc -c spectrum
  599. */
  600.  
  601. /*** Header Files ***/
  602. #include <dos.h>
  603. #include <stdio.h>
  604. #include <fcntl.h>
  605.  
  606. /*** Function Protypes ***/
  607. /* Used Locally */
  608. int allocmem( unsigned, unsigned * );
  609. int freemem ( unsigned );
  610. int _open( const char *, int oflags );
  611. int _close( int );
  612. /* Error trapper */
  613. extern void pascal error_init (void);
  614. extern unsigned pascal error_trap (void pascal (*execution_procedure)() );
  615. extern void pascal error_log (unsigned error_code);
  616. /* Heap */
  617. extern void pascal heap_init (unsigned starting_segment, unsigned segment_count);
  618. extern void far * pascal heap_alloc (unsigned paragraph_count);
  619. /* Symbol table */
  620. extern void pascal word_init (unsigned maximum_word_count);
  621. extern void pascal word_read (unsigned file_handle);
  622. extern void pascal word_scan (void pascal (*word_procedure)() );
  623. extern char far * pascal word_name (unsigned word_descriptor);
  624. extern unsigned pascal word_refcount (unsigned word_descriptor);
  625. extern unsigned pascal word_count (void);
  626. extern int pascal word_compref (unsigned word_desc1, unsigned word_desc2);
  627. /* Sorting procedure */
  628. extern void pascal sort_do (unsigned far *sort_array, unsigned sort_count,int pascal (*compare_procedure)() );
  629.  
  630. /*** Global Variables ***/
  631. /* Error table */
  632. char * error_table [] = {
  633. "Insufficient Memory\n",
  634. "Out of Hash Space\n",
  635. "File Read Error\n",
  636. "Usage: SPECTRUM filespec [filespec] ... [filespec]\n(filespec may have ?,*)\n"
  637. };
  638.  
  639. /* Arguments */
  640. int global_argc;
  641. char **global_argv;
  642.  
  643. /* Memory */
  644. unsigned segment_count;
  645. unsigned starting_segment;
  646.  
  647. /* Sort array */
  648. unsigned sort_index;
  649. unsigned far *sort_array;
  650.  
  651. /**** Procedures ****/
  652. /* Fill sort array with descriptors */
  653. void pascal array_fill(unsigned word_desc)
  654. {
  655.     sort_array[sort_index++] = word_desc;
  656. }
  657.  
  658. /* Main execution procedure */
  659. void pascal main2 (void)
  660. {
  661.     int i;
  662.     unsigned j;
  663.     int words = 0;
  664.     int file_handle;
  665.     if( global_argc < 2 ) {
  666.     error_log(4);
  667.     }
  668.     heap_init (starting_segment, segment_count);
  669.     word_init (32767);
  670.     for( i=1 ; i<global_argc ; i++ ) {
  671.     file_handle = _open (global_argv[i], O_RDONLY);
  672.     if (file_handle != -1 ) {
  673.         word_read( file_handle);
  674.         _close( file_handle );
  675.         } else {
  676.         error_log(3);
  677.         }
  678.     }
  679.  
  680.     /* Obtain array address */
  681.     sort_array = (unsigned far *)heap_alloc((word_count()+7)/8);
  682.     /* Fill array */
  683.     sort_index = 0;
  684.     word_scan(array_fill);
  685.     /* Sort array */
  686.     printf ("Sorting...\n");
  687.     sort_do (sort_array, sort_index, word_compref);
  688.  
  689.     /* Display output */
  690.     printf ("\nCount\tWord\n");
  691.     printf ("-----\t----\n");
  692.     for (i=0 ; i<sort_index-1 ; i++) {
  693.     j = word_refcount(sort_array[i]);
  694.     words = words + j;
  695.     printf ("%d",j);
  696.     printf ("\t");
  697.     printf ("%Fs",word_name(sort_array[i]));
  698.     printf ("\n");
  699.     }
  700.     printf ("\nTotal unique words:\t%d\n",sort_index);
  701.     printf ("Total words:\t\t%d\n",words);
  702. }
  703.  
  704. /* Main procedure */
  705. int main( int argc, char *argv[] )
  706. {
  707.     int i;
  708.     /* Copy arguments */
  709.     global_argc = argc;
  710.     global_argv = argv;
  711.     error_init();
  712.     segment_count = allocmem(65535,&starting_segment);
  713.     allocmem( segment_count, &starting_segment );
  714.     i = error_trap ( main2 );
  715.     if (i != 0) {
  716.     /* Print error message */
  717.     printf (error_table[i-1]);
  718.     }
  719.     freemem (starting_segment);
  720.     return (i);
  721. }
  722.  
  723.  
  724.  
  725. [LISTING SIX]
  726.  
  727. spectrum.exe: spectrum.obj heap.obj word.obj error.obj sort.obj
  728.   tlink /v \tc\lib\c0s+\tc\exa\wildargs+spectrum+heap+word+error+sort, spectrum,,\tc\lib\cs.lib;
  729. heap.obj: heap.asm
  730.     ta heap /mx /zi
  731. word.obj: word.asm
  732.     ta word /mx /zi
  733. error.obj: error.asm
  734.     ta error /mx /zi
  735. sort.obj: sort.asm
  736.     ta sort /mx /zi
  737. spectrum.obj: spectrum.c
  738.     tcc -c -v spectrum
  739.  
  740.